package top.atcra.rpc.config;

import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.yaml.snakeyaml.Yaml;
import top.atcra.rpc.compress.CompressorType;
import top.atcra.rpc.config.dto.AppConfigDTO;
import top.atcra.rpc.protection.loadBalancer.LoadBalanceType;
import top.atcra.rpc.serialization.SerializerType;

import java.io.InputStream;
import java.util.function.Function;

/**
 * 配置加载器，载入YAML配置并填充POJO配置类。
 * 支持运行时配置热刷新。
 *
 * @author atcra
 * @date 2025-3-22
 */
@Slf4j
public class ConfigLoader {
    private static volatile ConfigLoader configLoader = getInstance();
    private AppConfig cfg;

    private ConfigLoader() {
    }

    public static ConfigLoader getInstance() {
        if (configLoader == null) {
            synchronized (ConfigLoader.class) {
                if (configLoader == null) {
                    configLoader = new ConfigLoader();
                    configLoader.loadConfig();
                }
            }
        }
        return configLoader;
    }

    @Setter
    Function<AppConfig, Void> onConfigLoaded = null;

    private void loadConfig() {
        try (InputStream inputStream = ConfigLoader.class
                .getClassLoader()
                .getResourceAsStream("config.yaml")) {
            Yaml yaml = new Yaml();
            var cfgDTO = yaml.loadAs(inputStream, AppConfigDTO.class);
            copyDtoToConfig(cfgDTO);
            if (onConfigLoaded != null) {
                onConfigLoaded.apply(cfg);
            }
        } catch (Exception e) {
            log.error("Can't load yaml config {}", e.getMessage());
            throw new RuntimeException(e);
        }
    }

    public static AppConfig getConfig() {
        return configLoader.cfg;
    }

    public static void reload() {
        configLoader.loadConfig();
    }

    private void copyDtoToConfig(AppConfigDTO cfgDTO) throws NoSuchFieldException, IllegalAccessException {
        if (cfg == null) {
            cfg = new AppConfig();
        }
        for (var field : AppConfigDTO.class.getDeclaredFields()) {
            var tgtField = AppConfig.class.getDeclaredField(field.getName());
            tgtField.setAccessible(true);
            switch (field.getName()) {
                case "serializer" -> tgtField.set(cfg, SerializerType.valueOf((String) field.get(cfgDTO)).getId());
                case "compressor" -> tgtField.set(cfg, CompressorType.valueOf((String) field.get(cfgDTO)).getId());
                case "loadBalancer" -> tgtField.set(cfg, LoadBalanceType.valueOf((String) field.get(cfgDTO)).getId());
                default -> tgtField.set(this.cfg, field.get(cfgDTO));
            }
        }
    }

}

